iT邦幫忙

2022 iThome 鐵人賽

DAY 18
2
Software Development

從 Node.js 開發者到量化交易者:打造屬於自己的投資系統系列 第 18

Day 18 - 基本面關注點:每季 EPS、每月營收

  • 分享至 

  • xImage
  •  

前一天我們介紹 本益比股價淨值比殖利率 等評價股票的基本面指標,並示範如何從證交所與櫃買中心取得這些數據。不過需要注意的是,證交所與櫃買中心每日更新的本益比數據,是採用過去四季 EPS 算出來的「歷史本益比」而非「預估本益比」。這會有什麼問題呢?因為財務報表的數字是過去已經發生的事實,但是股價主要是反映對未來的預期。

預估本益比

我們在法人或券商提供的研究報告經常會看到所謂的「目標價」。如果是穩定獲利的公司,這些目標價往往都是經由 預估本益比 計算而來。我們在前一天已經介紹了本益比的算法是:

本益比 = 股價 / 每股盈餘

透過本益比的計算公式,我們可以得出股價的計算方式為:

股價 = 本益比 * 每股盈餘

當我們要評估公司未來的合理股價,就是給出一個 預期本益比 以及 預期每股盈餘

合理股價 = 預期本益比 * 預期每股盈餘

如果是評價一個成長潛力或正在成長的公司,我們會關注與往年同期相比的季度 EPS 是否逐年成長。當公司最新的財報發布時,市場就會關心財報數字是否符合預期或發生意外。

預期與意外

獲得兩屆全美投資大賽冠軍的 馬克.米奈爾維尼(Mark Minervini)在暢銷書籍《超級績效:金融怪傑交易之道》中提到:「股價會因為兩種理由而發生變動:預期(anticipation)與意外(surprise)。每個價格走勢都是因為這兩種因素而變動:預期某新聞、某事件、某重要基本面發生變動,或是對於利多或利空意外事件產生反應。

預期與意外會使股價發生變動,當公司發布財報時,市場的反應會有三種狀況:優於預期符合預期低於預期。如果財報數字出現顯著意外,負責追蹤的法人及分析師就會修正估值、更新目標價。當財報或月營收優於預期時,法人將上調獲利預估,EPS 被上修,目標價也跟著上調,此時容易吸引買盤進場,股價上漲機率高;當財報或月營收低於預期時,法人將下調獲利預估,EPS 下修使目標價也跟著下調,此時可能會有失望性賣壓,容易使股價下跌。

每季財報公佈的 EPS 是公司最重要的獲利數字之一,它會直接影響法人對公司的估值;而每月營收則是預估當季 EPS 重要的先行指標。對於自己實際持有的股票或考慮買進的標的,投資人應該關心公司公布財報與營收的時間,以及公司財報發布後市場的反應狀況。

要瞭解公司的營運狀況與獲利能力當然不僅每季 EPS 與每月營收數字,還需要針對公司的三大財務報告(綜合損益表、資產負債表、現金流量表)進行財務分析。不過這已經超出了本系列文的範圍,所以我們以投資人與市場最關心的公司每季 EPS、每月營收數字為例做說明。

每月營收及財報公布日期

會計是商業的語言,財務報告就是企業繳出的成績單。我國證交法規定,一般上市櫃公司應於每會計年度終了後 三個月內 公告 年度財務報告;於每會計年度第一季、第二季及第三季終了後 四十五日內 公告 季度財務報告;於 每月十日以前,公告並申報 上月份營運情形。一般上市櫃公司每月營收與財報公布日期如下:

  • 月營收:每月 10 日前
  • 去年度財報:3 月 31 日前
  • 第一季財報:5 月 15 日前
  • 第二季財報:8 月 14 日前
  • 第三季財報:11 月 14 日前
  • 第四季財報及年報:隔年 3 月 31 日前

查詢上市櫃公司每季 EPS

財務報告的三大報表是「資產負債表」、「綜合損益表」以及「現金流量表」。其中 綜合損益表 表達企業某段期間經營損益之狀況,我們可以在公司發布每一季的綜合損益表中,找到公司的每股盈餘(EPS)。

過去企業編製財務報告主要是採用我國一般公認會計原則(ROC GAAP),為了提升國內企業及國際企業間財務報告之比較性,自 2013 年起,國內上市櫃、興櫃公司正式適用國際財務報導準則(IFRSs)。

在公開資訊觀測站彙總報表的 綜合損益表 頁面,可以按市場別、年度及季別,查詢上市、上櫃、興櫃或公開發行公司的綜合損益表。

公開資訊觀測站首頁 > 彙總報表 > 財務報表 > 採IFRSs後 > 綜合損益表

在「綜合損益表」頁面選擇「市場別」、「年度」以及「季別」,按下「查詢」後,就會列出符合條件查詢的資料。

https://ithelp.ithome.com.tw/upload/images/20220918/2015015015nlM0wvYi.png

假如我們要查詢上市公司在 2022 年(民國 111 年)第二季的 EPS,在「市場別」選「上市」、「年度」選「111」、「季別」選「2」,按下「查詢」後,就會列出所有上市公司在 2022 年(民國 111 年)第二季資料的綜合損益表彙整資訊,在彙整表格的最後一欄就可以找到「基本每股盈餘(元)」,即公司該季的 EPS。

https://ithelp.ithome.com.tw/upload/images/20220918/20150150dtr43qltvI.png

我們可以打開終端機使用 curl 指令模擬表單請求:

$ curl --request POST \
    --url https://mops.twse.com.tw/mops/web/ajax_t163sb04 \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-raw 'encodeURIComponent=1&step=1&firstin=1&off=1&isQuery=Y&TYPEK=sii&year=111&season=02'

curl 指令的 data-raw 選項代表 HTTP POST 的資料,其中最重要的是以下三個欄位:

  • TYPEK:市場別,sii 表示上市公司;otc 表示上櫃公司,rotc 表示興櫃公司;pub 表示公開發行公司。
  • year:年度,以民國年表示。
  • season:季別,01 表示第一季;02 表示第二季;03 表示第三季;04 表示第四季。

執行上述 curl 指令後會回應 HTML 格式是資料輸出綜合損益表彙整資訊。由於公開資訊觀測站目前並沒有以 API 的形式提供資料,因此我們必須實作爬蟲程式取得所需資料欄位。

實作:取得上市櫃公司每季 EPS

首先我們新增一個 MopsScraperService 表示從公開資訊觀測站取得資料的服務。打開終端機,使用 Nest CLI 建立 MopsScraperService

$ nest g service scraper/mops-scraper --flat --no-spec 

因為我們先前在專案建立了 common lib,因此執行 Nest CLI 命令後可能會出現以下提示:

? Which project would you like to generate to?

選項會出現 src [ Default ]common,請選擇 src [ Default ] 即可。

然後 Nest CLI 會在 src/scraper 目錄下建立 mops-scraper.service.ts 檔案,並且將 MopsScraperService 加入至 ScraperModuleproviders 設定。

開啟 src/scraper/mops-scraper.service.ts 檔案,在 MopsScraperService 實作 fetchQuarterlyEps() 方法,並指定 options 選項,可選擇市場別、年度與月份,以取得公司當季綜合損益表,擷取基本每股盈餘:

import * as _ from 'lodash';
import * as cheerio from 'cheerio';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

type Market = 'sii' | 'otc' | 'rotc' | 'pub';

@Injectable()
export class MopsScraperService {
  constructor(private httpService: HttpService) { }

  async fetchQuarterlyEps(options: { market: Market, year: number, quarter: number }) {
    const { market, year, quarter } = options;

    // 建立 FormData
    const form = new URLSearchParams({
      encodeURIComponent: '1',
      step: '1',
      firstin: '1',
      off: '1',
      isQuery: 'Y',
      TYPEK: market,
      year: numeral(year).subtract(1911).format(),
      season: numeral(quarter).format('00'),
    });
    const url = 'https://mops.twse.com.tw/mops/web/t163sb04';

    // 取得 HTML 頁面
    const page = await firstValueFrom(this.httpService.post(url, form)).then(response => response.data);

    // 使用 cheerio 載入 HTML 以取得表格的 table rows
    const $ = cheerio.load(page);

    // 遍歷每個 table row 並將其轉換成我們想要的資料格式
    const data = $('.even,.odd').map((i, el) => {
      const td = $(el).find('td');
      const symbol = td.eq(0).text().trim();  // 公司代號
      const name = td.eq(1).text().trim();  // 公司名稱
      const eps = numeral(td.eq(td.length - 1).text().trim()).value();  // 基本每股盈餘(元)
      return { symbol, name, eps, year, quarter };
    }).toArray();

    return _.orderBy(data, 'symbol', 'asc');  // 依股票代號排序
  }
}

fetchQuarterlyEps() 方法中,參數需要指定 options 選項物件,包含以下內容:

  • market:市場別,sii 表示上市;otc 表示上櫃;rotc 表示興櫃;pub 表示公開發行
  • year:年度
  • quarter:季別

fetchQuarterlyEps() 方法以陣列形式回傳符合條件的公司 EPS 清單,每個陣列元素的物件包含以下內容:

  • symbol:股票代號
  • name:股票名稱
  • eps:基本稅後盈餘
  • year:年度
  • quarter:季度

完成後,我們只要呼叫 MopsScraperServicefetchQuarterlyEps() 方法,就可以按市場別、年度與季度取得公司當季 EPS。以下是上市公司在 2022 年第一季所公布之綜合損益表所取得的當季 EPS 為例:

[
  { symbol: '1101', name: '台泥', eps: 0.2, year: 2022, quarter: 1 },
  { symbol: '1102', name: '亞泥', eps: 0.76, year: 2022, quarter: 1 },
  { symbol: '1103', name: '嘉泥', eps: -0.12, year: 2022, quarter: 1 },
  { symbol: '1104', name: '環泥', eps: 0.82, year: 2022, quarter: 1 },
  { symbol: '1108', name: '幸福', eps: 0.14, year: 2022, quarter: 1 },
  { symbol: '1109', name: '信大', eps: 0.26, year: 2022, quarter: 1 },
  { symbol: '1110', name: '東泥', eps: 0.05, year: 2022, quarter: 1 },
  { symbol: '1201', name: '味全', eps: 0.19, year: 2022, quarter: 1 },
  { symbol: '1203', name: '味王', eps: 0.59, year: 2022, quarter: 1 },
  { symbol: '1210', name: '大成', eps: 0.69, year: 2022, quarter: 1 },
  ... 958 more items
]

查詢上市櫃公司每月營收

在公開資訊觀測站的 採用IFRSs後每月營業收入彙總表 頁面,可以按市場別、年度及月份,查詢上市、上櫃、興櫃或公開發行公司的當月營收。

公開資訊觀測站首頁 > 彙總報表 > 營運概況 > 每月營收 > 採用IFRSs後每月營業收入彙總表

在「採用IFRSs後每月營業收入彙總表」頁面選擇「市場別」、「年度」以及「月份」,按下「查詢」後,查詢結果會以另跳視窗顯示。

https://ithelp.ithome.com.tw/upload/images/20220918/20150150QetM2VaZyL.png

假如我們要查詢國內上市公司在 2022 年(民國 111 年)8 月份的營收,在「市場別」選「上市」、「年度」選「111」、「月份」選「8」,按下「查詢」後,查詢結果以另跳視窗顯示,並進入以下頁面。

https://ithelp.ithome.com.tw/upload/images/20220918/20150150KE5q3eNYux.png

以國內上市公司在 2022 年(民國 111 年)8 月份的營收資料,URL 位址是:

https://mops.twse.com.tw/nas/t21/sii/t21sc03_111_6_0.html

解析網址,我們可以發現這個 URL 對應的查詢條件是:

https://mops.twse.com.tw/nas/t21/{市場別}/t21sc03_{年度}_{月份}_{公司類型}.html

以上 URL 對應查詢條件的值說明如下:

  • 市場別sii 表示上櫃;otc 表示上櫃;rotc 表示興櫃;pub 表示公開發行公司。
  • 年度:即年度,使用民國年表示。
  • 月份:即月份。
  • 公司類型0 表示本國公司;1 表示外國公司。

由於公開資訊觀測站目前並沒有以 API 的形式提供當月營收資料,因此我們必須實作爬蟲程式取得所需資料欄位。

實作:取得上市櫃公司每月營收

開啟 src/scraper/mops-scraper.service.ts 檔案,在 TwseScraperService 實作 fetchMonthlyRevenue() 方法,並指定 options 選項,可選擇市場別、年、月份與公司類型,以取得公司當月營收:

import * as _ from 'lodash';
import * as cheerio from 'cheerio';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { firstValueFrom } from 'rxjs';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

type Market = 'sii' | 'otc' | 'rotc' | 'pub';

@Injectable()
export class MopsScraperService {
  constructor(private httpService: HttpService) { }

  ...

  async fetchMonthlyRevenue(options: { market: Market, year: number, month: number, type: number }) {
    const { market, year, month, type } = options;
    const suffix = `${numeral(year).subtract(1911).value()}_${month}_${type}`;
    const url = `https://mops.twse.com.tw/nas/t21/${market}/t21sc03_${suffix}.html`;

    // 取得 HTML 並轉換為 Big-5 編碼
    const page = await firstValueFrom(this.httpService.get(url, { responseType: 'arraybuffer' }))
      .then(response => iconv.decode(response.data, 'big5'));

    // 使用 cheerio 載入 HTML 以取得表格的 table rows
    const $ = cheerio.load(page);

    // 遍歷每個 table row 並轉換資料格式
    const data = $('tr [align=right]')
      .filter((i, el) => {
        const th = $(el).find('th');
        const td = $(el).find('td');
        return (th.length === 0) && !!td.eq(0).text();
      })
      .map((i, el) => {
        const td = $(el).find('td');
        const symbol = td.eq(0).text().trim();  // 公司代號
        const name = td.eq(1).text().trim();  // 公司名稱
        const revenue = numeral(td.eq(2).text().trim()).value(); // 當月營收
        return { symbol, name, revenue, year, month };
      })
      .toArray();

    return _.sortBy(data, 'symbol');  // 依股票代號排序
  }
}

fetchMonthlyRevenue() 方法中,需要指定 options 選項參數物件,包含以下內容:

  • market:市場別。sii 表示上市;otc 表示上櫃;rotc 表示興櫃;pub 表示公開發行
  • year:年度
  • month:月份
  • type:公司類型。0 是本國公司;1 是外國公司

fetchMonthlyRevenue() 方法以陣列形式回傳符合條件的公司當月營收清單,每個陣列元素的物件包含以下內容:

  • symbol:股票代號
  • name:股票名稱
  • revenue:營業收入
  • year:年度
  • month:月份

完成後,我們只要呼叫 MopsScraperServicefetchMonthlyRevenue() 方法,就可以按市場別、年份與月份取得公司當月營收。以下以國內上市公司在 2022 年 (民國 111 年) 8 月份的營收資料為例:

[
  { symbol: '1101', name: '台泥', revenue: 10689860, year: 2022, month: 8 },
  { symbol: '1102', name: '亞泥', revenue: 7069221, year: 2022, month: 8 },
  { symbol: '1103', name: '嘉泥', revenue: 182371, year: 2022, month: 8 },
  { symbol: '1104', name: '環泥', revenue: 605512, year: 2022, month: 8 },
  { symbol: '1108', name: '幸福', revenue: 390053, year: 2022, month: 8 },
  { symbol: '1109', name: '信大', revenue: 478276, year: 2022, month: 8 },
  { symbol: '1110', name: '東泥', revenue: 176935, year: 2022, month: 8 },
  { symbol: '1201', name: '味全', revenue: 2008672, year: 2022, month: 8 },
  { symbol: '1203', name: '味王', revenue: 546197, year: 2022, month: 8 },
  { symbol: '1210', name: '大成', revenue: 10578033, year: 2022, month: 8 }
  ... 882 more items
]

本日小結

  • 股價主要是反映對未來的預期,預估本益比是推算股價的一種方式。
  • 股價會因為兩種理由而發生變動:預期與意外。
  • 一般上市櫃公司在每月 10 日前會公布營收;第一、二、三季結束後 45 日內會公布季報;會計年度結束後 3 個月內會公布第四季財報以及上年度年報。
  • 瞭解如何在公開資訊觀測站上查詢綜合損益表,並實作取得上市櫃公司每季 EPS 的方法。
  • 瞭解如何在公開資訊觀測站上查詢並實作取得上市櫃公司每月營收的方法。

Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰
本系列文已正式出版為《Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰》。本書新增了全新內容和實用範例,為你提供更深入的學習體驗!歡迎參考選購,開始你的量化投資之旅!
天瓏網路書店連結:https://www.tenlong.com.tw/products/9786263336070


上一篇
Day 17 - 找出便宜標的:本益比、股價淨值比、殖利率
下一篇
Day 19 - 跟著大戶走:集保戶股權分散表
系列文
從 Node.js 開發者到量化交易者:打造屬於自己的投資系統31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言